package com.xinyuan.digit.next.subscriber.db.provider;

import com.xinyuan.digit.next.api.BaseEntity;
import com.xinyuan.digit.next.subscriber.db.pojo.Condition;
import com.xinyuan.digit.next.subscriber.db.pojo.OrderItem;
import com.xinyuan.digit.next.subscriber.db.util.LambdaUtils;
import com.xinyuan.digit.next.subscriber.db.util.LocalObjectUtil;
import com.xinyuan.digit.next.subscriber.db.wapper.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.jdbc.SQL;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * 通用数据库操作方法
 *
 * @author liuchenghui 2025/10/23
 * @version 1.0
 */
public class BaseDaoProvider<E extends BaseEntity> {
    public final static String IS_TRUE = "1";
    public final static String IS_FALSE = "0";

    /**
     * 查询单条数据
     *
     * @param wrapper
     * @return
     */
    public String selectOne(@Param("collection") LambdaQueryWrapper<?> wrapper) {
        SQL sql = new SQL();
        //表名
        String tableName = LambdaUtils.classToLine(wrapper.getEntityClass());
        sql.SELECT(LocalObjectUtil.getPropertyNameLines(wrapper.getEntityClass())).FROM(tableName).WHERE(LambdaUtils.column(BaseEntity::getDeleted) + " = " + IS_FALSE);
        buildWhereConditions(sql, wrapper);
        sql.LIMIT(1);
        return sql.toString();
    }

    /**
     * 根据ID查询数据
     *
     * @param id
     * @param clazz
     * @return
     */
    public String selectById(Long id, Class clazz) {
        return new SQL() {{
            SELECT(LocalObjectUtil.getPropertyNameLines(clazz));
            FROM(LambdaUtils.classToLine(clazz));
            WHERE(LambdaUtils.column(BaseEntity::getDeleted) + " = " + IS_FALSE);
            WHERE(LambdaUtils.column(BaseEntity::getId) + " = " + id);
        }}.toString();
    }

    /**
     * 查询所有数据
     *
     * @param wrapper
     * @return
     */
    public String selectAll(@Param("collection") LambdaQueryWrapper<?> wrapper) {
        SQL sql = new SQL();
        // 设置表名
        String tableName = LambdaUtils.classToLine(wrapper.getEntityClass());
        sql.SELECT(LocalObjectUtil.getPropertyNameLines(wrapper.getEntityClass())).FROM(tableName).WHERE(LambdaUtils.column(BaseEntity::getDeleted) + " = " + IS_FALSE);
        // 构建WHERE条件
        buildWhereConditions(sql, wrapper);
        // 构建ORDER BY条件
        buildOrderBy(sql, wrapper);
        return sql.toString();
    }

    /**
     * 查询分页
     *
     * @param wrapper
     * @param start
     * @param length
     * @return
     */
    public String selectPage(@Param("collection") LambdaQueryWrapper<?> wrapper, int start, int length) {
        SQL sql = new SQL();
        // 设置表名
        String tableName = LambdaUtils.classToLine(wrapper.getEntityClass());
        sql.SELECT(LocalObjectUtil.getPropertyNameLines(wrapper.getEntityClass())).FROM(tableName).WHERE(LambdaUtils.column(BaseEntity::getDeleted) + " = " + IS_FALSE);
        // 构建WHERE条件
        buildWhereConditions(sql, wrapper);
        // 构建ORDER BY条件
        buildOrderBy(sql, wrapper);
        sql.OFFSET(start).LIMIT(length);
        return sql.toString();
    }

    /**
     * 查询条目数
     *
     * @param wrapper
     * @return
     */
    public String selectPageCount(@Param("collection") LambdaQueryWrapper<?> wrapper) {
        SQL sql = new SQL();
        String tableName = LambdaUtils.classToLine(wrapper.getEntityClass());
        sql.SELECT("COUNT(" + LambdaUtils.column(BaseEntity::getId) + ")").FROM(tableName).WHERE(LambdaUtils.column(BaseEntity::getDeleted) + " = " + IS_FALSE);
        buildWhereConditions(sql, wrapper);
        return sql.toString();
    }

    /**
     * 插入数据
     *
     * @param entity
     * @param clazz
     * @return
     */
    public String insert(@Param("entity") E entity, Class clazz) {
        String sql;
        Set<String> notNullPropertyNameSet = LocalObjectUtil.getNotNullPropertyNameSet(clazz, entity);
        sql = new SQL() {{
            INSERT_INTO(LambdaUtils.classToLine(clazz));
            notNullPropertyNameSet.forEach(e -> {
                //忽略字段
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getUpdateAt))) return;
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getCreateAt))) return;
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getReleased))) return;
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getDeleted))) return;
                VALUES(LambdaUtils.camelToUnderline(e), "#{entity." + LambdaUtils.toLowerCaseFirstOne(e) + "}");
            });
            INTO_COLUMNS(LambdaUtils.column(BaseEntity::getUpdateAt), LambdaUtils.column(BaseEntity::getCreateAt), LambdaUtils.column(BaseEntity::getReleased), LambdaUtils.column(BaseEntity::getDeleted));
            INTO_VALUES("now()", "now()", IS_TRUE, IS_FALSE);
        }}.toString();
        return sql;
    }

    /**
     * 根据ID更新数据
     *
     * @param entity
     * @param clazz
     * @return
     */
    public String updateById(@Param("entity") E entity, @Param("id") Long id, Class clazz) {
        String sql;
        Set<String> notNullPropertyNameSet = LocalObjectUtil.getNotNullPropertyNameSet(clazz, entity);
        sql = new SQL() {{
            UPDATE(LambdaUtils.classToLine(clazz));
            notNullPropertyNameSet.forEach(e -> {
                //忽略字段
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getUpdateAt))) return;
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getCreateAt))) return;
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getReleased))) return;
                if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getDeleted))) return;
                SET(LambdaUtils.camelToUnderline(e) + "= #{entity." + LambdaUtils.toLowerCaseFirstOne(e) + "}");
            });
            SET(LambdaUtils.column(BaseEntity::getUpdateAt) + " = now()");
            WHERE(LambdaUtils.column(BaseEntity::getId) + " = " + id);
        }}.toString();
        return sql;
    }

    /**
     * 启用禁用
     *
     * @param id
     * @param isReleased
     * @param clazz
     * @return
     */
    public String enable(Long id, boolean isReleased, Class clazz) {
        return new SQL() {{
            UPDATE(LambdaUtils.classToLine(clazz));
            SET(LambdaUtils.column(BaseEntity::getReleased) + " = " + (isReleased ? IS_TRUE : IS_FALSE), LambdaUtils.column(BaseEntity::getUpdateAt) + " = now()");
            WHERE(LambdaUtils.column(BaseEntity::getId) + " = " + id);
        }}.toString();
    }

    /**
     * 根据ID删除数据
     *
     * @param id
     * @param clazz
     * @return
     */
    public String delById(Long id, Class clazz) {
        return new SQL() {{
            UPDATE(LambdaUtils.classToLine(clazz));
            SET(LambdaUtils.column(BaseEntity::getDeleted) + " = " + IS_TRUE, LambdaUtils.column(BaseEntity::getUpdateAt) + " = now()");
            WHERE(LambdaUtils.column(BaseEntity::getId) + " = " + id);
        }}.toString();
    }

    /**
     * 批量删除
     *
     * @param wrapper
     * @return
     */
    public String delBatch(@Param("collection") LambdaQueryWrapper<?> wrapper) {
        SQL sql = new SQL() {{
            String tableName = LambdaUtils.classToLine(wrapper.getEntityClass());
            UPDATE(tableName);
            SET(LambdaUtils.column(BaseEntity::getDeleted) + " = " + IS_TRUE, LambdaUtils.column(BaseEntity::getUpdateAt) + " = now()");
        }};
        buildWhereConditions(sql, wrapper);
        return sql.toString();
    }

    /**
     * 物理删除多条(谨慎使用)
     *
     * @param wrapper
     * @return
     */
    public String erase(@Param("collection") LambdaQueryWrapper<?> wrapper) {
        SQL sql = new SQL() {{
            String tableName = LambdaUtils.classToLine(wrapper.getEntityClass());
            DELETE_FROM(tableName);
        }};
        buildWhereConditions(sql, wrapper);
        return sql.toString();
    }

    /**
     * 批量插入
     *
     * @param entityCollection
     * @param clazz
     * @return
     */
    public String insertBatch(@Param("collection") Collection<E> entityCollection, Class clazz) {
        List<String> columnList = LocalObjectUtil.getPropertyNameList(clazz, new ArrayList<>());
        StringBuffer columnName = new StringBuffer();
        StringBuffer columnValue = new StringBuffer();
        columnList.forEach(e -> {
            //忽略字段
            if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getUpdateAt))) return;
            if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getCreateAt))) return;
            if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getReleased))) return;
            if (e.equalsIgnoreCase(LambdaUtils.column(BaseEntity::getDeleted))) return;
            columnName.append(LambdaUtils.camelToUnderline(e)).append(",");
            columnValue.append(", #{collection[__0].").append(e).append("}");
        });
        columnName.append(LambdaUtils.column(BaseEntity::getUpdateAt)).append(",");
        columnName.append(LambdaUtils.column(BaseEntity::getCreateAt)).append(",");
        columnName.append(LambdaUtils.column(BaseEntity::getReleased)).append(",");
        columnName.append(LambdaUtils.column(BaseEntity::getDeleted));
        columnValue.append(", now(), now(), ").append(IS_TRUE).append(",").append(IS_TRUE);
        String valueTmp = "(" + columnValue.substring(1) + ")";

        StringBuilder value = new StringBuilder();
        for (int i = 0; i < entityCollection.size(); i++) {
            value.append(valueTmp.replaceAll("__0", i + ""));
            if (entityCollection.size() - 1 > i) value.append(",");
        }
        return "INSERT INTO " + LambdaUtils.classToLine(clazz) + " (" + columnName + ") " + " VALUES " + value;
    }


    private void buildWhereConditions(SQL sql, LambdaQueryWrapper<?> wrapper) {
        List<Condition> conditions = wrapper.getConditions();
        for (int i = 0; i < conditions.size(); i++) {
            Condition condition = conditions.get(i);
            sql.WHERE(condition.toSQL(i));
        }
    }

    private void buildOrderBy(SQL sql, LambdaQueryWrapper<?> wrapper) {
        List<OrderItem> orderItems = wrapper.getOrderItems();
        if (!orderItems.isEmpty()) {
            StringBuilder orderByBuilder = new StringBuilder();
            for (int i = 0; i < orderItems.size(); i++) {
                OrderItem orderItem = orderItems.get(i);
                if (i > 0) {
                    orderByBuilder.append(", ");
                }
                orderByBuilder.append(orderItem.toSQL());
            }
            sql.ORDER_BY(orderByBuilder.toString());
        }
    }
}
